昨天提到了瀏覽器中 事件循環(Event Loop)
整體的運作過程,但其中有個地方沒有深入介紹,那就是Macrotask
和 MicroTask
。
注意,接下來會提到多種非同步操作,例如計時器(setTimeout、setInterval)
、Promise
、DOM事件處理(例如滑鼠點擊、鍵盤輸入的監聽器)
、非同步函數(async 和 await)
等等。本篇文章並不會詳述這些非同步操作的用法,而會聚焦到今天的主題 Macrotask
和 MicroTask
的差別與運作流程。
另外今天算是延續昨天的 事件循環(Event Loop)
的概念,最好先理解一下。
開始今天的主題,在事件循環中的 Callback Queue
中,
其實可以看成有兩條佇列:Macrotask(宏任務)
和Microtask(微任務)
。
非同步操作會依照類型不同而被排入兩條佇列的其中一條;而他們又會依照彼此的優先度而被取出執行。
Macrotask
是一個較大、較長的非同步任務,且在事件循環中具有較低的優先度。
例如:計時器(setTimeOut、setInterval),使用者互動(事件監聽器的 callback)等。
Microtask
是一個較小、較短的非同步任務,它的優先度較高,會在 Macrotask
之前執行。
例如:Promise 的then和catch、async function、process.nextTick()等。
以下是 Event Loop 處理 Macrotask
和 Microtask
流程圖(未知原始圖片來源)
上圖簡單來說,必須等所有的 microtask 佇列清空,才會檢查 macrotask 佇列並取出一個項目並執行。
但每執行完一個 macrotask ,都會再檢查 microtask 佇列並等待當下的 microtask 佇列清空,才會再回頭檢查 macrotask 佇列。
用動圖更好理解:
以下是一個實際的例子。
console.log('Start');
setTimeout(() => console.log('SetTimeout checked!'), 0);
Promise.resolve().then(() => console.log('Promise checked!'));
console.log('End');
而它們輸出順序的結果為:
Start
End
Promise checked!
SetTimeout checked!
首先我們知道setTimeout 和 Promise 都是非同步操作。
但因為 setTimeout 屬於 Macrotask
,Promise 屬於 Microtask
,
因此雖然 setTimeout 的回調函式先進入了 Callback Stack
,
但 Promise 具有較高的優先級,
所以會先印出「Promise checked!」後才印出「SetTimeout checked!」。
今天介紹了非同步操作依類型會被分為 Macrotask
與 MicroTask
。
而MicroTask 的優先度高於 Macrotask,所以 事件循環(Event Loop)
在檢查 Call Stack
時會優先取出 MicroTask 並執行。
那麼今天就到這邊了,明天見 ~